﻿/**
 * 主程序，测试虚拟机的运行
 *
 * 创建时间：2022年5月21日
 * 作者：李述铜
 * 联系邮箱: 527676163@qq.com
 */
#include <stdio.h>
#include <stdlib.h>
#include "simu8051.h"
#include "simu8051_tools.h"

// nop/sjmp
static uint8_t t0_nop_code[] = {0x00, 0x00, 0x80, 0xFE};

#define REG_SP			0x1000
#define REG_A			0x1001
#define REG_B			0x1002
#define REG_PSW			0x1003
#define REG_PC			0x1004
#define REG_DPTR		0x1005
#define CYCLE		    0x1006
#define REG_R0			0x2000
#define REG_R1			0x2001
#define REG_R2			0x2002
#define REG_R3			0x2003
#define REG_R4			0x2004
#define REG_R5			0x2005
#define REG_R6			0x2006
#define REG_R7			0x2007
#define REG_END         0x2FFF

void check_result(void) {
    uint16_t addr = 0x600;
    do {
        uint8_t addr0 = simu8051_read(MEM_TYPE_CODE, addr++);
        uint8_t addr1 = simu8051_read(MEM_TYPE_CODE, addr++);
        uint8_t data0 = simu8051_read(MEM_TYPE_CODE, addr++);
        uint8_t data1 = simu8051_read(MEM_TYPE_CODE, addr++);
        uint16_t cmp_addr = (addr0 << 8) | addr1;
        uint16_t expect_data = (data0 << 8) | data1;
        uint16_t real_data;

        switch (cmp_addr) {
        case REG_SP:
            real_data = simu8051_read(MEM_TYPE_SFR, SFR_SP);
            break;
        case REG_A:
            real_data = simu8051_read(MEM_TYPE_SFR, SFR_ACC);
            break;
        case REG_B:
            real_data = simu8051_read(MEM_TYPE_SFR, SFR_B);
            break;
        case REG_PSW:
            real_data = simu8051_read(MEM_TYPE_SFR, SFR_PSW);
            break;
        case REG_PC:
            real_data = simu8051_pc();
            break;
        case REG_DPTR:
            real_data = simu8051_read(MEM_TYPE_SFR, SFR_DPH) << 8;
            real_data |= simu8051_read(MEM_TYPE_SFR, SFR_DPL);
            break;
        case CYCLE:
            real_data = simu8051_cycle();
            break;
        case REG_R0:
            real_data = simu8051_read(MEM_TYPE_IRAM, 0);
            break;
        case REG_R1:
            real_data = simu8051_read(MEM_TYPE_IRAM, 1);
            break;
        case REG_R2:
            real_data = simu8051_read(MEM_TYPE_IRAM, 2);
            break;
        case REG_R3:
            real_data = simu8051_read(MEM_TYPE_IRAM, 3);
            break;
        case REG_R4:
            real_data = simu8051_read(MEM_TYPE_IRAM, 4);
            break;
        case REG_R5:
            real_data = simu8051_read(MEM_TYPE_IRAM, 5);
            break;
        case REG_R6:
            real_data = simu8051_read(MEM_TYPE_IRAM, 6);
            break;
        case REG_R7:
            real_data = simu8051_read(MEM_TYPE_IRAM, 7);
            break;
        case REG_END:
            return;
        default:
            real_data = simu8051_read(MEM_TYPE_IRAM, cmp_addr);
            break;
        }

        if (real_data != expect_data) {
            printf("test error: addr=%x, real=%x, expect=%x\n",
                cmp_addr, real_data, expect_data);
            exit(-1);
        }
    } while (1); 
}

static const char* file_name[] = {
    "../../test/t1_simple/Objects/code.hex",
    "../../test/t2_move_0/Objects/code.hex",
    "../../test/t2_move_1/Objects/code.hex",
    "../../test/t2_move_2/Objects/code.hex",
    "../../test/t2_move_3/Objects/code.hex",
    "../../test/t3_movc_0/Objects/code.hex",
    "../../test/t4_movx_0/Objects/code.hex",
    "../../test/t5_push_pop/Objects/code.hex",
    "../../test/t6_xch/Objects/code.hex",
    "../../test/t7_anl/Objects/code.hex",
    "../../test/t8_orl/Objects/code.hex",
    "../../test/t9_xrl/Objects/code.hex",
    "../../test/t10_cpl_rr_rl/Objects/code.hex",
    "../../test/t11_add_0/Objects/code.hex",
    "../../test/t11_add_1/Objects/code.hex",
    "../../test/t12_inc/Objects/code.hex",
    "../../test/t13_dec/Objects/code.hex",
    "../../test/t14_subb_0/Objects/code.hex",
    "../../test/t14_subb_1/Objects/code.hex",
    "../../test/t15_mul_div_da/Objects/code.hex",
    "../../test/t16_jmp_call_ret/Objects/code.hex",
    "../../test/t17_djnz_jz_cjne/Objects/code.hex",
    "../../test/t18_bit_jb_jc/Objects/code.hex",
};

void test_memory(void) {
    static uint8_t data[MEM_XRAM_SIZE];

    for (int i = 0; i < 128; i++) {
        simu8051_write(MEM_TYPE_BIT, i, 1);
        data[i] = simu8051_read(MEM_TYPE_BIT, i);
    }

    for (int i = 128; i < 256; i++) {
        simu8051_write(MEM_TYPE_BIT, i, 1);
        data[i] = simu8051_read(MEM_TYPE_BIT, i);
    }

    for (int i = 0; i < MEM_IRAM_SIZE; i++) {
        simu8051_write(MEM_TYPE_IRAM, i, i);
    }
    for (int i = 0; i < MEM_IRAM_SIZE; i++) {
        data[i] = simu8051_read(MEM_TYPE_IRAM, i);
    }

    for (int i = 0; i < MEM_XRAM_SIZE; i++) {
        simu8051_write(MEM_TYPE_XRAM, i, i);
        data[i] = simu8051_read(MEM_TYPE_XRAM, i);
    }

    simu8051_write(MEM_TYPE_SFR, SFR_ACC, 0x12);
    data[0] = simu8051_read(MEM_TYPE_SFR, SFR_ACC);

}

static void show_disa(instr_t* instr) {
    const instr_info_t* info = instr->info;
    printf("c:%2d\n pc: %x \t", simu8051_cycle(), simu8051_pc());
    if (info->bytes == 1) {
        printf("o: %02x\t i: %s", instr->opcode, info->disa);
    }
    else if (info->bytes == 2) {
        printf("o: %02x%02x\t i: %s", instr->opcode, instr->op0, info->disa);
    }
    else {
        printf("o: %02x%02x%02x\t i: %s", instr->opcode, instr->op0, instr->op1, info->disa);
    }
    
    putchar('\n');
}

void test_instr(void) {
    for (int i = 0; i < sizeof(file_name) / sizeof(const char*); i++) {
        uint16_t pc;
        uint8_t* code;

        printf("begin test: %s\n", file_name[i]);

        simu8051_reset();
        code = simu8051_load_hexfile(file_name[i]);
        if (code == NULL) {
            printf("load hex file failed:%s", file_name[i]);
            exit(-1);
        }


        simu8051_load(code);
        do {
            instr_t instr;

            pc = simu8051_pc();
            simu8051_fetch_instr(&instr);
            if (instr.info->bytes == 0) {
                printf("unknown instruction. opcode=%x", instr.opcode);
                exit(-1);
            }
            show_disa(&instr);
            simu8051_exec(&instr);

            simu8051_dump_regs();
        } while (pc != simu8051_pc());

        check_result();

        free(code);
        printf("test end.\n");
    }
}

void test_program(void) {
    //const char* file_name = "../../test/t19_serial_0/Objects/code.hex";
    //const char* file_name = "../../test/t19_serial_1/Objects/code.hex";
    // const char* file_name = "../../test/t20_timer/Objects/code.hex";
    const char* file_name = "../../test/t21_int/Objects/code.hex";

    uint8_t* code = simu8051_load_hexfile(file_name);
    if (code == NULL) {
        printf("load hex file failed: %s", file_name);
        exit(-1);
    }

    simu8051_reset();
    simu8051_load(code);
    simu8051_run(0, 0);
}

int main() {
    simu8051_init();
    //test_memory();
    //test_instr();
    test_program();

    return 0;
}

void simu8051_uart_write(char c) {
    putchar(c);
}

#include <conio.h>

int simu8051_uart_rx_rdy(void) {
    return _kbhit();
}

char simu8051_uart_read(void) {
    return _kbhit() ? _getch() : 0;
}